home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Taifun / Taifun 159 (1991-03-10)(Manewaldt, A.)(DE)(PD).zip / Taifun 159 (1991-03-10)(Manewaldt, A.)(DE)(PD).adf / SnoopDos / src / snoopdos.c < prev    next >
C/C++ Source or Header  |  1998-03-07  |  33KB  |  1,161 lines

  1. /*
  2.  *        SNOOPDOS.C
  3.  *
  4.  *        (C) Copyright Eddy Carroll, May 1990. Freely distributable.
  5.  *
  6.  *        Snoopdos patches into dos.library and outputs a message to a
  7.  *        debugging window or file whenever a process calls certain DOS
  8.  *        functions.
  9.  *
  10.  *        Type snoopdos -h for a list of available options. See the
  11.  *        documentation for further details.
  12.  *
  13.  *        Compiles under Lattice C V5.10. I use flags: -cusq -j88i -ms -v
  14.  *        Note: Source assumes tabs set every 4 columns.
  15.  *
  16.  *        Revisions
  17.  *        ---------
  18.  *        30 Oct 90:  Fixed potential deadlock problem in termination code
  19.  *                    Improved handling of NULL lock (i.e. boot device)
  20.  *
  21.  *        27 Jan 91:    Really fixed deadlock problem in termination code!
  22.  */
  23.  
  24. #ifndef LATTICE_50
  25. #include "system.h"
  26. #endif
  27.  
  28. /*
  29.  *        Assorted strings
  30.  */
  31. #define TITLE \
  32. "SnoopDos V1.2 (C) Copyright Eddy Carroll, Jan 1991. Freely distributable."
  33.  
  34. char *HEADER =
  35. "Process name          Func  Filename                                Mode Res."
  36. "\r\n"
  37. "------------          ----  --------                                ---- ----"
  38. "\r\n";
  39.  
  40. #define PORTNAME    "SnoopDos Port"
  41.  
  42. #define DEFWINDOW    "CON:0/0/640/120/"
  43.  
  44. /*
  45.  *        The following message array contains both colour and non-colour
  46.  *        versions of the various short message strings displayed.
  47.  */
  48. char *msgs[][2] = {
  49. /* Monochrome           Colour   */
  50. /* ----------           ------   */
  51.     "OLD ",                "OLD ",
  52.     "NEW ",                "\033[33mNEW\033[0m ",
  53.     "R/W ",                "\033[32mR/W\033[0m ",
  54.     "??? ",                "??? ",
  55.     "SHAR",                "SHAR",
  56.     "EXCL",                "\033[33mEXCL\033[0m",
  57.     "????",                "????",
  58.     "Okay\r\n",            "Okay\r\n",
  59.     "Fail\r\n",            "\033[33mFail\033[0m\r\n",
  60.     ">",                "\033[33m>\033[0m",
  61.     "> (Done)",            "> (Done)",
  62.     "Warning: Missed",    "\033[33mWarning:\033[0m Missed",
  63.     ">>>>\r\n",            ">>>>\r\n",
  64.     "Open",                "Open",
  65.     "Lock",                "\033[33mLock\033[0m",
  66.     "Load",                "\033[32mLoad\033[0m",
  67.     "Exec",                "\033[32mExec\033[0m",
  68.     "CD  ",                "CD  ",
  69.     "Del ",                "\033[33mDel\033[0m "
  70. };
  71.  
  72. #define TXT_OLD        msgs[ 0][colour]
  73. #define TXT_NEW        msgs[ 1][colour]
  74. #define TXT_R_W        msgs[ 2][colour]
  75. #define TXT_QM3        msgs[ 3][colour]
  76. #define TXT_SHAR    msgs[ 4][colour]
  77. #define TXT_EXCL    msgs[ 5][colour]
  78. #define TXT_QM4        msgs[ 6][colour]
  79. #define TXT_OKAY    msgs[ 7][colour]
  80. #define TXT_FAIL    msgs[ 8][colour]
  81. #define TXT_POINT    msgs[ 9][colour]
  82. #define TXT_DONE    msgs[10][colour]
  83. #define TXT_WARN    msgs[11][colour]
  84. #define TXT_NEST    msgs[12][colour]
  85. #define TXT_OPEN    msgs[13][colour]
  86. #define TXT_LOCK    msgs[14][colour]
  87. #define TXT_LOAD    msgs[15][colour]
  88. #define TXT_EXEC    msgs[16][colour]
  89. #define TXT_CURDIR    msgs[17][colour]
  90. #define TXT_DELETE    msgs[18][colour]
  91.  
  92. #define POINT(x)    ((x) ? TXT_POINT : " ")
  93.  
  94. /*
  95.  *        Now some standard system-type macros
  96.  */
  97. #define reg_d0    register __d0
  98. #define reg_d1    register __d1
  99. #define reg_d2    register __d2
  100. #define reg_d3    register __d3
  101. #define reg_a0    register __a0
  102.  
  103. #define BTOC(x)    (void *)(((ULONG)x) << 2)
  104.  
  105. #define D_S(name, type) char c_##name[sizeof(type)+3];\
  106.                         type *name = (type *)((long)(c_##name+3) & ~3)
  107.  
  108. extern __asm BPTR CallOpen(reg_d1 UBYTE *, reg_d2 int);
  109. extern __asm BPTR CallLock(reg_d1 UBYTE *, reg_d2 int);
  110. extern __asm BPTR CallLoadSeg(reg_d1 UBYTE *);
  111. extern __asm LONG CallExecute(reg_d1 UBYTE *, reg_d2 BPTR, reg_d3 BPTR);
  112. extern __asm BPTR CallCurrentDir(reg_d1 BPTR);
  113. extern __asm LONG CallDeleteFile(reg_d1 UBYTE *);
  114.  
  115. /*
  116.  *        Structure used to pass messages back and fro
  117.  */
  118. typedef struct {
  119.     struct Message msg;            /* Standard message header    */
  120.     struct Process *process;    /* Sending process id        */
  121.     int msgtype;                /* Message type, see below    */
  122.     int  data1;                    /* Data field 1                */ 
  123.     void *data2;                /* Data field 2                */
  124. } MYMSG;
  125.  
  126. /*
  127.  *        Now the various settings that can be set to affect the monitoring
  128.  */
  129. typedef struct {
  130.     int set_doopen;                /* If true, monitor Open()            */
  131.     int    set_dolock;                /* If true, monitor Lock()            */
  132.     int set_doloadseg;            /* If true, monitor LoadSeg()        */
  133.     int set_doexecute;            /* If true, monitor Execute()        */
  134.     int set_docurdir;            /* If true, monitor CurrentDir()    */
  135.     int set_dodelete;            /* If true, monitor DeleteFile()    */
  136.     int    set_showfullpath;        /* If true, display full paths        */
  137.     int set_sleepwait;            /* If true, sleep if necessary        */
  138.     int set_snoopactive;        /* If true, monitoring is active    */
  139.     int set_colour;                /* If true, use ANSI colour codes    */
  140. } SETTINGS;
  141.  
  142. /*
  143.  *        Default settings
  144.  */
  145. SETTINGS settings = { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1 };
  146.  
  147. /*
  148.  *        These defines allow the various settings to be accessed as
  149.  *        normal variables rather than structure members; this is purely
  150.  *        for convenience.
  151.  */
  152. #define doopen            settings.set_doopen
  153. #define dolock            settings.set_dolock
  154. #define doloadseg        settings.set_doloadseg
  155. #define doexecute        settings.set_doexecute
  156. #define docurdir        settings.set_docurdir
  157. #define dodelete        settings.set_dodelete
  158. #define showfullpath    settings.set_showfullpath
  159. #define sleepwait        settings.set_sleepwait
  160. #define snoopactive        settings.set_snoopactive
  161. #define colour            settings.set_colour
  162.  
  163. /*
  164.  *        Now the various message types that can be sent
  165.  */
  166. typedef enum {
  167.     MSG_QUIT,            /* Quit                     */
  168.     MSG_GETOPTIONS,        /* Read options                */
  169.     MSG_SETOPTIONS,        /* Update options            */
  170.     MSG_OPEN,            /* Open file                */
  171.     MSG_OPEN_DONE,        /* Open file completed        */
  172.     MSG_LOCK,            /* Lock file                */
  173.     MSG_LOCK_DONE,        /* Lock file completed        */
  174.     MSG_LOADSEG,        /* LoadSeg file                */
  175.     MSG_LOADSEG_DONE,    /* LoadSeg file completed    */
  176.     MSG_EXECUTE,        /* Execute command            */
  177.     MSG_CURDIR,            /* CurrentDir                */
  178.     MSG_DELETE,            /* DeleteFile                */
  179.     MSG_DELETE_DONE,    /* DeleteFile completed        */
  180. } MSGTYPES;
  181.  
  182. struct SignalSemaphore sem[1];
  183. struct MsgPort   *myport;        /* Pointer to background SnoopDos msg port    */
  184. struct MsgPort   *inport;        /* Pointer to our own message port            */
  185. struct Task      *snooptask;    /* Pointer to our own task                    */
  186. BPTR debugwin;                    /* Output file or window                    */
  187. BPTR stderr;                    /* Standard CLI console                        */
  188. int  extfile;                    /* True if output directed to external file    */
  189. char extfilename[100];            /* Name of window/file to open if specified    */
  190. char outbuf[800];                /* Output buffer used by myprintf() etc.    */
  191.  
  192. int missed_open_sig;            /* Signal to indicate Open() missed            */
  193. int missed_lock_sig;            /* Signal to indicate Lock() missed            */
  194. int missed_loadseg_sig;            /* Signal to indicate LoadSeg() missed        */
  195. int missed_execute_sig;            /* Signal to indicate Execute() missed        */
  196. int missed_curdir_sig;            /* Signal to indicate CurrentDir() missed    */
  197. int missed_delete_sig;            /* Signal to indicate DeleteFile() missed    */
  198. int portsig;                    /* Signal used by our public port            */
  199.  
  200. /**************************** Start of Functions ****************************/
  201.  
  202. /*
  203.  *        cleanup()
  204.  *        ---------
  205.  *        Cleans up all active resources and exits. If err is -1, then
  206.  *        returns instead of exiting.
  207.  */
  208. void cleanup(int err)
  209. {
  210.     static int called = 0;
  211.  
  212.     if (called++)        /* Make sure not called twice by accident */
  213.         return;
  214.  
  215.     if (inport)
  216.         DeletePort(inport);
  217.  
  218.     if (debugwin)
  219.         Close(debugwin);
  220.  
  221.     if (stderr)
  222.         Close(stderr);
  223.  
  224.     if (err != -1)
  225.         exit(err);
  226. }
  227.  
  228. /*
  229.  *        myprintf(), myfprintf()
  230.  *        -----------------------
  231.  *        Two low cost alternatives to printf that go directly to AmigaDOS
  232.  *        Note we deliberately use the pre-ANSI declaration, to avoid Lattice
  233.  *        kicking up a fuss about the wrong number of parameters.
  234.  */
  235. void myprintf(format, p1, p2, p3, p4, p5, p6)
  236. UBYTE *format;
  237. ULONG p1, p2, p3, p4, p5, p6;
  238. {
  239.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  240.     Write(Output(), outbuf, strlen(outbuf));
  241. }
  242.  
  243. void myfprintf(file, format, p1, p2, p3, p4, p5, p6)
  244. BPTR file;
  245. char *format;
  246. ULONG p1, p2, p3, p4, p5, p6;
  247. {
  248.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  249.     Write(file, outbuf, strlen(outbuf));
  250. }
  251.  
  252. /*
  253.  *        sendmsg()
  254.  *        ---------
  255.  *        Sends a message to myport, and waits for a reply to arrive.
  256.  *        A message type and some message data are all that need be provided
  257.  *        by the caller; the callee will provide the rest. Doesn't return
  258.  *        until a reply has been received.
  259.  */
  260. void sendmsg(int type, int data1, void *data2)
  261. {
  262.     MYMSG msg;
  263.     struct Process *me = (struct Process *)FindTask(0);
  264.     struct MsgPort *replyport = &me->pr_MsgPort;
  265.  
  266.     msg.msg.mn_Node.ln_Type = NT_MESSAGE;
  267.     msg.msg.mn_Length       = sizeof(MYMSG);
  268.     msg.msg.mn_ReplyPort    = replyport;
  269.     msg.process                = me;
  270.     msg.msgtype             = type;
  271.     msg.data1               = data1;
  272.     msg.data2               = data2;
  273.  
  274.     PutMsg(myport, &msg);
  275.     WaitPort(replyport);
  276.     GetMsg(replyport);
  277. }
  278.  
  279.  
  280. /*
  281.  *        getlockpath()
  282.  *        -------------
  283.  *        Returns a pointer to a string containing the full path for the
  284.  *        specified lock. You must copy the string before calling this
  285.  *        routine again.
  286.  */
  287. char *getlockpath(BPTR lock)
  288. {
  289.     struct Process *p = (struct Process *)FindTask(0L);
  290.     APTR oldwin = p->pr_WindowPtr;
  291.     static char path[300];
  292.     int pos = 299;
  293.     BPTR mylock;
  294.     char *name;
  295.     D_S(fib, struct FileInfoBlock);
  296.     int err = 0;
  297.  
  298.     p->pr_WindowPtr = (APTR)-1;    /* Disable error requesters */
  299.     mylock = DupLock(lock);
  300.  
  301.     path[pos] = '\0';
  302.  
  303.     do {
  304.         int len;
  305.         BPTR newlock;
  306.  
  307.         if (!Examine(mylock, fib)) {
  308.             UnLock(mylock);
  309.             err++;
  310.             break;
  311.         }
  312.         name = fib->fib_FileName;
  313.         if (*name == '\0')
  314.             name = "RAM";        /* Workaround for old RAM: disk bug */
  315.         len = strlen(name);
  316.         pos = pos - len - 1;
  317.         newlock = ParentDir(mylock);
  318.         UnLock(mylock);
  319.         mylock = newlock;
  320.         strncpy(path + pos, name, len);
  321.         if (mylock)
  322.             path[pos + len] = '/';
  323.         else
  324.             path[pos + len] = ':';
  325.     } while (mylock);
  326.     p->pr_WindowPtr = oldwin;    /* Enable error requesters again */
  327.  
  328.     if (err) {
  329.         /*
  330.          *        Volume not present so have to be happy with just
  331.          *        returning the volume node instead.
  332.          */
  333.         struct FileLock *fl;
  334.         struct DeviceList *dl;
  335.         UBYTE *name;
  336.  
  337.         if (lock) {
  338.             fl = BTOC(lock);
  339.             dl = BTOC(fl->fl_Volume);
  340.             name = BTOC(dl->dl_Name);
  341.  
  342.             strncpy(path, name + 1, *name);
  343.             path[*name] = '\0';
  344.         } else {
  345.             strcpy(path, "<Boot device>");
  346.         }
  347.         strcat(path, ":.../");
  348.         return (path);
  349.     } else
  350.         return (path + pos);
  351. }
  352.  
  353. /*
  354.  *        makepath()
  355.  *        ----------
  356.  *        Builds a full path string given a process ptr and a filename.
  357.  *        If the filename includes relative references (// or :)
  358.  *        these are handled also. The point of this is to resolve relative
  359.  *        directory references.
  360.  *
  361.  *        Returns TRUE if a new string was generated, FALSE if the existing
  362.  *        string didn't depend on the current directory (this doesn't affect
  363.  *        the output stored in buf though).
  364.  */
  365. int makepath(char *buf, BPTR curdir, char *filename)
  366. {
  367.     char *origfilename = filename;
  368.     int pos;
  369.     int doneroot = 0;
  370.  
  371.     /*
  372.      *        Special check for the 'current process console' file '*'
  373.      */
  374.     if (strcmp(filename, "*") == 0) {
  375.         strcpy(buf, filename);
  376.         return (FALSE);
  377.     }
  378.  
  379.     strcpy(buf, getlockpath(curdir));
  380.     pos = strlen(buf);
  381.  
  382.     for (;;) {
  383.         if (!doneroot && *filename == ':') {
  384.             /*
  385.              *        Path is relative to root
  386.              */
  387.             doneroot = 1;
  388.             for (pos = 0; buf[pos] && buf[pos] != ':'; pos++)
  389.                 ;
  390.             if (buf[pos] == ':')
  391.                 pos++;
  392.         } else if (*filename == '/') {
  393.             /*
  394.              *        Path is relative to parent directory; if none, then
  395.              *        remains the same.
  396.              */
  397.             int newpos = pos - 2;
  398.             while (newpos >= 0 && buf[newpos] != '/' && buf[newpos] != ':')
  399.                 newpos--;
  400.             if (newpos >= 0)
  401.                 pos = newpos + 1;
  402.         } else {
  403.             /*
  404.              *        No more special characters; just append what's left of
  405.              *        the filename.
  406.              */
  407.             if (!doneroot) {
  408.                 /*
  409.                  *        If the filename wasn't relative to the root
  410.                  *        directory, then make sure there are no device/volume
  411.                  *        references contained within it. We copy the original
  412.                  *        filename rather than the currently modified version
  413.                  *        since a volume name of /A: is legal and the / would
  414.                  *        be stripped off the modified name.
  415.                  */
  416.                 char *p;
  417.                 for (p = filename; *p; p++) {
  418.                     if (*p == ':') {
  419.                         strcpy(buf, origfilename);
  420.                         return (0);
  421.                     }
  422.                 }
  423.             }
  424.             strcpy(buf + pos, filename);
  425.             return (1);
  426.         }
  427.         filename++;
  428.     }
  429. }
  430.  
  431. /*
  432.  *        mainloop()
  433.  *        ----------
  434.  *        This is the main event loop for SnoopDOS, where everything happens.
  435.  *        Control is passed here when SnoopDOS first starts up. When this
  436.  *        function returns, it terminates the background process.
  437.  */
  438. void mainloop(void)
  439. {
  440.     static char fullname[300];
  441.     int done   = 0;                    /* True if finished processing            */
  442.     int col0   = 1;                    /* True if cursor in column 0            */
  443.     int nested = 0;                    /* True if nested DOS calls                */
  444.  
  445. #define MISSED_NONE        0            /* Haven't missed anything    */
  446. #define MISSED_OPEN        (1 << 0)    /* Missed Open() call        */
  447. #define MISSED_LOCK        (1 << 1)    /* Missed Lock() call        */
  448. #define MISSED_LOADSEG    (1 << 2)    /* Missed LoadSeg() call    */
  449. #define MISSED_EXECUTE    (1 << 3)    /* Missed Execute() call    */
  450. #define MISSED_CURDIR    (1 << 4)    /* Missed CurrentDir() call    */
  451. #define MISSED_DELETE    (1 << 5)    /* Missed DeleteFile() call    */
  452.  
  453.     int missed = MISSED_NONE;        /* Was a DOS function missed? See above    */
  454.  
  455.     inport = CreatePort(PORTNAME, 0);
  456.     if (!inport) {
  457.         myfprintf(stderr, "SnoopDos: Can't allocate message port.\n");
  458.         cleanup(-1);
  459.         return;
  460.     }
  461.  
  462.     myport  = inport;
  463.     portsig = 1 << myport->mp_SigBit;
  464.  
  465.     /*
  466.      *        Allocate signals
  467.      */
  468.     missed_open_sig       = 1 << AllocSignal(-1);
  469.     missed_lock_sig       = 1 << AllocSignal(-1);
  470.     missed_loadseg_sig = 1 << AllocSignal(-1);
  471.     missed_execute_sig = 1 << AllocSignal(-1);
  472.     missed_curdir_sig  = 1 << AllocSignal(-1);
  473.     missed_delete_sig  = 1 << AllocSignal(-1);
  474.  
  475.     if (    missed_open_sig    == -1  || missed_lock_sig    == -1  ||
  476.             missed_loadseg_sig == -1  || missed_execute_sig == -1  ||
  477.             missed_curdir_sig  == -1  || missed_delete_sig  == -1) {
  478.         myfprintf(stderr, "SnoopDos: Can't allocate enough signals.\n");
  479.         cleanup(-1);
  480.         return;
  481.     }
  482.  
  483.     if (extfile) {
  484.         debugwin = Open(extfilename, MODE_NEWFILE);
  485.         if (!debugwin) {
  486.             myfprintf(stderr, "SnoopDos: Can't open file %s for output.\n",
  487.                                 extfilename);
  488.             cleanup(-1);
  489.             return;
  490.         }
  491.         myfprintf(debugwin, "\r\n" TITLE "\r\n\r\n");
  492.     } else {
  493.         debugwin = Open(DEFWINDOW TITLE, MODE_NEWFILE);
  494.         if (!debugwin) {
  495.             myfprintf(stderr, "SnoopDos: Can't open display window.\n");
  496.             cleanup(-1);
  497.             return;
  498.         }
  499.     }
  500.     Close(stderr); stderr = NULL;
  501.  
  502.     if (!extfile) {
  503.         myfprintf(debugwin, "\n"
  504.     "Type CTRL-E/CTRL-D to enable/disable snooping. Type CTRL-C to exit.\n\n");
  505.     };
  506.     myfprintf(debugwin, HEADER);
  507.  
  508.     InitSemaphore(sem);
  509.     snooptask = FindTask(0L);
  510.     installdospatch();
  511.  
  512.     /*
  513.      *        Now just sit processing messages
  514.      */
  515.     while (!done) {
  516.         int mask;
  517.  
  518. #define SIGMASK    (portsig | missed_open_sig | missed_lock_sig | \
  519.                  missed_loadseg_sig | missed_execute_sig |     \
  520.                  missed_curdir_sig | missed_delete_sig |       \
  521.                  SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E)
  522.  
  523.         mask = Wait(SIGMASK);
  524.  
  525.         if (mask & SIGBREAKF_CTRL_C)
  526.             done = 1;
  527.  
  528.         if (mask & SIGBREAKF_CTRL_D) {
  529.             if (snoopactive) {
  530.                 myfprintf(debugwin,
  531. "\nSnooping now disabled; type CTRL-E to enable it again.\r\n\r\n");
  532.                 snoopactive = 0;
  533.             }
  534.         }
  535.  
  536.         if (mask & SIGBREAKF_CTRL_E) {
  537.             if (!snoopactive) {
  538.                 myfprintf(debugwin,
  539. "Snooping now enabled; type CTRL-D to disable it again.\r\n\r\n%s", HEADER);
  540.                 snoopactive = 1;
  541.             }
  542.         }
  543.  
  544.         if (mask & missed_open_sig)
  545.             missed |= MISSED_OPEN;
  546.  
  547.         if (mask & missed_lock_sig)
  548.             missed |= MISSED_LOCK;
  549.  
  550.         if (mask & missed_loadseg_sig)
  551.             missed |= MISSED_LOADSEG;
  552.  
  553.         if (mask & missed_execute_sig)
  554.             missed |= MISSED_EXECUTE;
  555.  
  556.         if (mask & missed_curdir_sig)
  557.             missed |= MISSED_CURDIR;
  558.  
  559.         if (mask & missed_delete_sig)
  560.             missed |= MISSED_DELETE;
  561.  
  562.         if (mask & portsig) {
  563.             MYMSG *msg;
  564.  
  565.             /*
  566.              *        Note in the following, there is some slightly tricky
  567.              *        stuff to handle the case where a DOS function calls
  568.              *        another DOS function internally. Under 1.3, this doesn't
  569.              *        happen, but it may under WB 2.0 and in addition, Jim
  570.              *        Goodnow's handy Rez utility patches LoadSeg() so that
  571.              *        it calls Open(). `nested' is true when a nested call
  572.              *        like this happens, and in this case, process names are
  573.              *        printed out prefixed by `> ' to indicate the nesting.
  574.              *
  575.              *        The nesting is detected because an incoming Open/Lock/
  576.              *        LoadSeg message will arrive when the cursor is positioned
  577.              *        to print a Fail/Okay result from a previous call (i.e.
  578.              *        col0 is false; the cursor is not in column 0). When a
  579.              *        result arrives in and the cursor IS in column 0, then
  580.              *        this must be the result from an earlier call so the
  581.              *        nesting is ended. Note that the semaphores used ensures
  582.              *        that this situation can only ever occur as a result of
  583.              *        nesting; other tasks will always wait until they are
  584.              *        properly in sync before sending messages to the main
  585.              *        Snoopdos process.
  586.              *
  587.              *        The more alert among you are probably saying something
  588.              *        like "Ah, but what if SnoopDos is configured to display
  589.              *        all DOS calls, even if it means forcing some tasks to
  590.              *        sleep until SnoopDos can process them? Then, when a
  591.              *        task calls LoadSeg() (say) and LoadSeg() calls Open(),
  592.              *        Open() will sleep forever waiting for the semaphore
  593.              *        Obtain()ed by LoadSeg() to become free again." Well,
  594.              *        in fact, it won't, since exec's ObtainSemaphore() call
  595.              *        will suceed even when the semaphore is in use, IF the
  596.              *        caller is the task that owns the semaphore -- and in such
  597.              *        a case, the caller will be the same task. (It took me a
  598.              *        while to figure out exactly what things were working --
  599.              *        it looked like I should get a serious lockup in certain
  600.              *        circumstances).
  601.              *        
  602.              */
  603.             while ((msg = (MYMSG *)GetMsg(myport)) != NULL) {
  604.                 /*
  605.                  *        Get the name of the process/command for
  606.                  *        printing out if necessary.
  607.                  */
  608.                 static char namebuf[256];
  609.                 UBYTE *procname = msg->process->pr_Task.tc_Node.ln_Name;
  610.                 struct CommandLineInterface *cli = BTOC(msg->process->pr_CLI);
  611.                 BPTR curdir = msg->process->pr_CurrentDir;
  612.                 UBYTE *s;
  613.                 UBYTE *filename;
  614.                 int newname;    /* If true, a new name was generated */
  615.  
  616.                 if (!col0 && (msg->msgtype == MSG_OPEN        ||
  617.                               msg->msgtype == MSG_LOCK        ||
  618.                               msg->msgtype == MSG_LOADSEG    ||
  619.                               msg->msgtype == MSG_CURDIR    ||
  620.                               msg->msgtype == MSG_DELETE)) {
  621.                     nested = 1;
  622.                     myfprintf(debugwin, TXT_NEST);
  623.                 }
  624.                 if (cli && cli->cli_Module) {
  625.                     UBYTE *cliname = BTOC(cli->cli_CommandName);
  626.                     if (*cliname > 0) {
  627.                         /* Only use CLI name if it's not null */
  628.                         strncpy(namebuf+2, cliname + 1, *cliname);
  629.                         namebuf[*cliname+2] = '\0';
  630.                         procname = namebuf + 2;
  631.                         if (nested) {
  632.                             /*
  633.                              *        If we're not at column 0, then we're
  634.                              *        calling this DOS function from within
  635.                              *        another DOS function so indent display
  636.                              */
  637.                             procname = namebuf;
  638.                             procname[0] = '>';
  639.                             procname[1] = ' ';
  640.                         }
  641.                     }
  642.                 } else {
  643.                     if (nested) {
  644.                         sprintf(namebuf, "> %s", procname);
  645.                         procname = namebuf;
  646.                     }
  647.                 }
  648.  
  649.                 /*
  650.                  *        Now handle the message
  651.                  */
  652.                 filename = msg->data2;    /* Standard file name            */
  653.                 newname  = 0;            /* No problems expanding it        */
  654.  
  655.                 switch (msg->msgtype) {
  656.  
  657.                     case MSG_QUIT:
  658.                         done = 1;
  659.                         break;
  660.  
  661.                     case MSG_GETOPTIONS:
  662.                         memcpy(msg->data2, &settings, sizeof(SETTINGS));
  663.                         break;
  664.  
  665.                     case MSG_SETOPTIONS:
  666.                         memcpy(&settings, msg->data2, sizeof(SETTINGS));
  667.                         break;
  668.  
  669.                     case MSG_OPEN:
  670.                         if (msg->data1 == MODE_OLDFILE)
  671.                             s = TXT_OLD;
  672.                         else if (msg->data1 == MODE_NEWFILE)
  673.                             s = TXT_NEW;
  674.                         else if (msg->data1 == MODE_READWRITE)
  675.                             s = TXT_R_W;
  676.                         else
  677.                             s = TXT_QM3;
  678.  
  679.                         if (showfullpath) {
  680.                             newname = makepath(fullname, curdir, msg->data2);
  681.                             filename = fullname;
  682.                         }
  683.                         myfprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  684.                                     TXT_OPEN, POINT(newname), filename, s);
  685.                         col0 = 0;
  686.                         break;  
  687.  
  688.                     case MSG_LOCK:
  689.                         if (msg->data1 == ACCESS_READ)
  690.                             s = TXT_SHAR;
  691.                         else if (msg->data1 == ACCESS_WRITE)
  692.                             s = TXT_EXCL;
  693.                         else
  694.                             s = TXT_QM4;
  695.  
  696.                         if (showfullpath) {
  697.                             newname = makepath(fullname, curdir, msg->data2);
  698.                             filename = fullname;
  699.                         }
  700.                         myfprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  701.                                     TXT_LOCK, POINT(newname), filename, s);
  702.                         col0 = 0;
  703.                         break;
  704.  
  705.                     case MSG_LOADSEG:
  706.                         if (showfullpath) {
  707.                             newname = makepath(fullname, curdir, msg->data2);
  708.                             filename = fullname;
  709.                         }
  710.                         myfprintf(debugwin, "%-21s %s %s%-44s ", procname,
  711.                                     TXT_LOAD, POINT(newname), filename);
  712.                         col0 = 0;
  713.                         break;
  714.  
  715.                     case MSG_EXECUTE:
  716.                         myfprintf(debugwin, "%-21s %s  %s\r\n", procname,
  717.                                                     TXT_EXEC, filename);
  718.                         col0 = 1;
  719.                         break;
  720.  
  721.                     case MSG_CURDIR:
  722.                         {
  723.                             char *dirname = getlockpath(msg->data1);
  724.                             int i = strlen(dirname);
  725.  
  726.                             if (dirname[i-1] == '/')
  727.                                 dirname[i-1] = '\0';
  728.                             myfprintf(debugwin, "%-21s %s  %s\r\n", procname,
  729.                                                     TXT_CURDIR, dirname);
  730.                             col0 = 1;
  731.                         }
  732.                         break;
  733.  
  734.                     case MSG_DELETE:
  735.                         if (showfullpath) {
  736.                             newname = makepath(fullname, curdir, msg->data2);
  737.                             filename = fullname;
  738.                         }
  739.                         myfprintf(debugwin, "%-21s %s %s%-44s ", procname,
  740.                                     TXT_DELETE, POINT(newname), filename);
  741.                         col0 = 0;
  742.                         break;
  743.  
  744.                     case MSG_LOCK_DONE:
  745.                     case MSG_OPEN_DONE:
  746.                     case MSG_LOADSEG_DONE:
  747.                     case MSG_DELETE_DONE:
  748.                         if (col0 && nested) {
  749.                             myfprintf(debugwin, "%-73s", TXT_DONE);
  750.                             nested = 0;
  751.                         }
  752.                         if (msg->data1)
  753.                             myfprintf(debugwin, TXT_OKAY);
  754.                         else
  755.                             myfprintf(debugwin, TXT_FAIL);
  756.                         col0 = 1;
  757.                         break;
  758.                 }
  759.                 ReplyMsg(msg);
  760.             }
  761.         }
  762.  
  763.         /*
  764.          *        Finally, check if we missed a DOS function. If we did,
  765.          *        AND we are in column 0, print out an appropriate message.
  766.          *        Otherwise, wait until next time. This stops the display
  767.          *        getting messed up when waiting to print a 'Fail' or 'Okay'.
  768.          */
  769.         if (missed) {
  770.             if (col0) {
  771.                 if (missed & MISSED_OPEN)
  772.                     myfprintf(debugwin, "%s an Open()\r\n", TXT_WARN);
  773.                 if (missed & MISSED_LOCK)
  774.                     myfprintf(debugwin, "%s a Lock()\r\n", TXT_WARN);
  775.                 if (missed & MISSED_LOADSEG)
  776.                     myfprintf(debugwin, "%s a LoadSeg()\r\n", TXT_WARN);
  777.                 if (missed & MISSED_EXECUTE)
  778.                     myfprintf(debugwin, "%s an Execute()\r\n", TXT_WARN);
  779.                 if (missed & MISSED_CURDIR)
  780.                     myfprintf(debugwin, "%s a CurrentDir()\r\n", TXT_WARN);
  781.                 if (missed & MISSED_DELETE)
  782.                     myfprintf(debugwin, "%s a DeleteFile()\r\n", TXT_WARN);
  783.                 missed = MISSED_NONE;
  784.             }
  785.         }
  786.     }
  787.  
  788.     /*
  789.      *        Remove the port from the public ports list; this stops any
  790.      *        SnoopDos processes started up elsewhere from trying to
  791.      *        communicate with us (which may happen if this process can't
  792.      *        exit immediately).
  793.      */
  794.     RemPort(myport);
  795.     myport->mp_Node.ln_Name = NULL;
  796.     snoopactive = 0;    /* Make sure our patch stops sending msgs to us! */
  797.  
  798.     /*
  799.      *        Now try and obtain our semaphore. Until we can achieve it,
  800.      *        we may still be receiving messages from other tasks telling
  801.      *        us about DOS operations, so, we reply to these as they arrive
  802.      *        (else deadlock occurs).
  803.      */
  804.     while (!AttemptSemaphore(sem)) {
  805.         MYMSG *msg = (MYMSG *)GetMsg(myport);
  806.  
  807.         if (msg)
  808.             ReplyMsg(msg);
  809.         Delay(5);                /* Wait for 0.2 seconds */
  810.     }
  811.  
  812.     /*
  813.      *        Now remove the dospatch, and cleanup
  814.      */
  815.     if (!uninstalldospatch()) {
  816.         /*
  817.          *        Someone else has patched DOSbase so print a message for
  818.          *        the user and keep trying every 5 seconds until we succeed.
  819.          */
  820.         int count = 0;
  821.  
  822.         myfprintf(debugwin,
  823. "\r\n"
  824. "Someone else has patched dos.library so I'll have to wait until they quit."
  825. "\r\n");
  826.         if (!extfile) {
  827.             myfprintf(debugwin, 
  828.   "This window will close shortly though to stop it from annoying you.\r\n");
  829.         }
  830.  
  831.         do {
  832.             Delay(50);                    /* Wait for one second */
  833.             if (debugwin) {
  834.                 count++;
  835.                 if (count > 10) {
  836.                     Close(debugwin);
  837.                     debugwin = NULL;
  838.                 }
  839.             }
  840.         } while (!uninstalldospatch());
  841.     }
  842.  
  843.     if (debugwin)
  844.         myfprintf(debugwin, "\r\nSnoopDos terminated.\r\n");
  845.  
  846.     /*
  847.      *        We do a final wait for 0.2 seconds, just in case their are any
  848.      *        other tasks still running in our SetFunction'd code.
  849.      *        (This isn't 100% bulletproof, but it's fairly safe).
  850.      */
  851.     Delay(10);
  852.     cleanup(-1);
  853. }
  854.  
  855. /*
  856.  *        main()
  857.  *        ------
  858.  *        Mainline. Parses the command line and, if necessary, kicks off the
  859.  *        background task to do the real work.
  860.  */
  861. void main(int argc, char **argv)
  862. {
  863.     int error  = 0;            /* True if error detected in cmd line    */
  864.     int quit   = 0;            /* True if we want to quit                */
  865.     int colsel = 0;            /* True if colour option was specified    */
  866.  
  867.     /*
  868.      *        See if we are already active elsewhere; if yes, then read in
  869.      *        the default settings used by that process.
  870.      */
  871.  
  872.     myport = FindPort(PORTNAME);
  873.     if (myport)
  874.         sendmsg(MSG_GETOPTIONS, 0, &settings);
  875.  
  876.     while (argc > 1 && argv[1][0] == '-') {
  877.         int val;
  878.  
  879.         /* Make -X == -x0 and -x == -x1 */
  880.         if (argv[1][1] >= 'a' && argv[1][2] != '0')
  881.             val = 1;
  882.         else
  883.             val = 0;
  884.  
  885.         switch (tolower(argv[1][1])) {
  886.  
  887.             case 'a': colour         = val;
  888.                       colsel        = 1;   break;    /* Display ANSI colour    */
  889.             case 'c': docurdir        = val; break;    /* Monitor CurrentDir() */
  890.             case 'd': dodelete        = val; break;    /* Monitor DeleteFile()    */
  891.             case 'f': showfullpath    = val; break;    /* Show full filepath    */
  892.             case 'g': doloadseg        = val; break;    /* Monitor LoadSeg()    */
  893.             case 'h': error            = 1;   break;    /* Just print help msg    */
  894.             case 'l': dolock        = val; break;    /* Monitor Lock()        */
  895.             case 'm': snoopactive    = val; break;    /* Actively monitoring    */
  896.             case 'o': doopen        = val; break;    /* Monitor Open()        */
  897.             case 'q': quit            = 1;   break;    /* Ask SnoopDos to quit */
  898.             case 'r': error            = 2;   break;    /* Report settings        */
  899.             case 'w': sleepwait        = val; break;    /* Wait when necessary    */
  900.             case 'x': doexecute        = val; break;    /* Monitor Execute()    */
  901.             case 'z':
  902.                     if (argv[1][2]) {
  903.                         strcpy(extfilename, &argv[1][2]);
  904.                         extfile = 1;
  905.                     } else {
  906.                         argv++;
  907.                         argc--;
  908.                         if (argc > 1) {
  909.                             strcpy(extfile, argv[1]);
  910.                             extfile = 1;
  911.                         }
  912.                     }
  913.                     if (!extfile) {
  914.                         myprintf(
  915.         "-z must be followed by a filename. Type SnoopDos -h for help.\n");
  916.                         exit(5);
  917.                     }
  918.                     /*
  919.                      *        If outputting to a file, make colour off
  920.                      *        by default, rather than on.
  921.                      */
  922.                     if (colsel == 0)
  923.                         colour = 0;
  924.                     break;
  925.  
  926.             default:
  927.                 myprintf("Unrecognised option %s. "
  928.                          "Type Snoopdos -h for help.\n", argv[1]);
  929.                 exit(5);
  930.         }
  931.         argv++; argc--;
  932.     }
  933.  
  934.     if (argc > 1)
  935.         error = 1;
  936.  
  937.     if (error) {
  938.         if (error == 1) {
  939.             myprintf(TITLE "\n\n"
  940. "SnoopDos monitors calls made to various AmigaDOS functions by all processes\n"
  941. "on the system. The following options are available. Use -x1 or just -x to\n"
  942. "enable an option, -x0 or -X to disable that option. Current settings in (-)."
  943. "\n\n");
  944.         } else
  945.             myprintf("Current SnoopDos settings:\n\n");
  946.  
  947. #define O(x,y) myprintf(x, y ? "(on)" : "(off)")
  948. O("    -a  Use ANSI colour sequences                     %s\n", colour);
  949. O("    -c  Monitor CurrentDir() calls                    %s\n", docurdir);
  950. O("    -d  Monitor DeleteFile() calls                    %s\n", dodelete);
  951. O("    -f  Display full filename paths                   %s\n", showfullpath);
  952. O("    -g  Monitor LoadSeg() calls                       %s\n", doloadseg);
  953. O("    -l  Monitor Lock() calls                          %s\n", dolock);
  954. O("    -m  Globally enable/disable monitoring            %s\n", snoopactive);
  955. O("    -o  Monitor Open() calls                          %s\n", doopen);
  956. O("    -w  Display all activity, sleeping if necessary   %s\n", sleepwait);
  957. O("    -x  Monitor Execute() calls                       %s\n", doexecute);
  958.  
  959.         if (error == 1) {
  960.             myprintf("\n"
  961. "    -h  Print out this help message\n"
  962. "    -q  Tell SnoopDos to quit\n"
  963. "    -r  Report current SnoopDos settings\n" 
  964. "    -z% Output to file % (e.g. -zCON:0/0/640/100/SnoopDos or -zSER:)\n"
  965. "\n");
  966.         }
  967.         cleanup(5);
  968.     }
  969.  
  970.     /*
  971.      *        First see are we already installed. If so, send a copy of the
  972.      *        updated settings to the background process, or send a QUIT
  973.      *        message if the user wanted to quit.
  974.      */
  975.     if (myport) {
  976.         if (quit)
  977.             sendmsg(MSG_QUIT, 0, 0);
  978.         else
  979.             sendmsg(MSG_SETOPTIONS, 0, &settings);
  980.         cleanup(0);
  981.     }
  982.  
  983.     /*
  984.      *        If user wanted to quit and we weren't already running, just
  985.      *        quit without doing anything.
  986.      */
  987.     if (quit) {
  988.         myprintf("There is no background SnoopDos process to tell to quit!\n");
  989.         cleanup(0);
  990.     }
  991.  
  992.     /*
  993.      *        Not installed, so install ourselves. First of all though, we
  994.      *        kick ourselves into the background and return control to the
  995.      *        calling CLI. We need to do this before we start creating ports
  996.      *        and opening files, to prevent Exec and AmigaDOS getting
  997.      *        confused about which task we are.
  998.      *
  999.      *        Also open stderr channel for the new task to output fatal
  1000.      *        error messages to; the new task takes responsibility for closing
  1001.      *        this channel.
  1002.      */
  1003.     stderr = Open("*", MODE_NEWFILE);
  1004.     if (!res("SnoopDos", 5, mainloop, 4000)) {
  1005.         myprintf("Couldn't spawn background SnoopDos task.\n");
  1006.         cleanup(10);
  1007.     }
  1008. }
  1009.  
  1010. /*
  1011.  *        NewOpen()
  1012.  *        ---------
  1013.  *        This is the new Open() code. It checks to see if the calling task
  1014.  *        is actually the SnoopDos task; if it is, then it essentially does
  1015.  *        nothing (this stops any possible deadlock occurring). Otherwise,
  1016.  *        it sends a message to the task with the pertinent info.
  1017.  *
  1018.  *        If sleeping is disabled then if the semaphore isn't immediately
  1019.  *        available, a signal is sent to the Snoopdos task telling it that
  1020.  *        it's missed trapping a function.
  1021.  */
  1022. __asm BPTR NewOpen(reg_d1 char *filename, reg_d2 int mode)
  1023. {
  1024.     BPTR filehandle;
  1025.     geta4();
  1026.     if (snoopactive && doopen && FindTask(0) != snooptask) {
  1027.         if (sleepwait)
  1028.             ObtainSemaphore(sem);
  1029.         else if (!AttemptSemaphore(sem)) {
  1030.             Signal(snooptask, missed_open_sig);
  1031.             return (CallOpen(filename, mode));
  1032.         }
  1033.         sendmsg(MSG_OPEN, mode, filename);
  1034.         filehandle = CallOpen(filename, mode);
  1035.         sendmsg(MSG_OPEN_DONE, filehandle, 0);
  1036.         ReleaseSemaphore(sem);
  1037.         return (filehandle);
  1038.     } else {
  1039.         return (CallOpen(filename, mode));
  1040.     }
  1041. }
  1042. /*
  1043.  *        NewLock()
  1044.  *        ---------
  1045.  *        Replacement for Lock(), all the comments for NewOpen() apply
  1046.  *        equally here.
  1047.  */
  1048. __asm BPTR NewLock(reg_d1 char *filename, reg_d2 int mode)
  1049. {
  1050.     BPTR lock;
  1051.     geta4();
  1052.     if (snoopactive && dolock && FindTask(0) != snooptask) {
  1053.         if (sleepwait)
  1054.             ObtainSemaphore(sem);
  1055.         else if (!AttemptSemaphore(sem)) {
  1056.             Signal(snooptask, missed_lock_sig);
  1057.             return (CallLock(filename, mode));
  1058.         }
  1059.         sendmsg(MSG_LOCK, mode, filename);
  1060.         lock = CallLock(filename, mode);
  1061.         sendmsg(MSG_LOCK_DONE, lock, 0);
  1062.         ReleaseSemaphore(sem);
  1063.         return (lock);
  1064.     } else {
  1065.         return (CallLock(filename, mode));
  1066.     }
  1067. }
  1068.  
  1069. /*
  1070.  *        NewLoadSeg()
  1071.  *        ------------
  1072.  *        Replacement for Lock(), all the comments for NewOpen() apply
  1073.  *        equally here.
  1074.  */
  1075. __asm BPTR NewLoadSeg(reg_d1 char *filename)
  1076. {
  1077.     BPTR seglist;
  1078.     geta4();
  1079.     if (snoopactive && doloadseg && FindTask(0) != snooptask) {
  1080.         if (sleepwait)
  1081.             ObtainSemaphore(sem);
  1082.         else if (!AttemptSemaphore(sem)) {
  1083.             Signal(snooptask, missed_loadseg_sig);
  1084.             return (CallLoadSeg(filename));
  1085.         }
  1086.         sendmsg(MSG_LOADSEG, 0, filename);
  1087.         seglist = CallLoadSeg(filename);
  1088.         sendmsg(MSG_LOADSEG_DONE, seglist, 0);
  1089.         ReleaseSemaphore(sem);
  1090.         return (seglist);
  1091.     } else {
  1092.         return (CallLoadSeg(filename));
  1093.     }
  1094. }
  1095.  
  1096. /*
  1097.  *        NewExecute()
  1098.  *        ------------
  1099.  *        Replacement for Execute()
  1100.  */
  1101. __asm LONG NewExecute(reg_d1 char *command, reg_d2 BPTR in, reg_d3 BPTR out)
  1102. {
  1103.     geta4();
  1104.     if (snoopactive && doexecute && FindTask(0) != snooptask) {
  1105.         if (sleepwait)
  1106.             ObtainSemaphore(sem);
  1107.         else if (!AttemptSemaphore(sem)) {
  1108.             Signal(snooptask, missed_execute_sig);
  1109.             return(CallExecute(command, in, out));
  1110.         }
  1111.         sendmsg(MSG_EXECUTE, 0, command);
  1112.         ReleaseSemaphore(sem);
  1113.     }
  1114.     return(CallExecute(command, in, out));
  1115. }
  1116.  
  1117. /*
  1118.  *        NewCurrentDir()
  1119.  *        ---------------
  1120.  *        Replacement for CurrentDir()
  1121.  */
  1122. __asm BPTR NewCurrentDir(reg_d1 BPTR newlock)
  1123. {
  1124.     geta4();
  1125.     if (snoopactive && docurdir && FindTask(0) != snooptask) {
  1126.         if (sleepwait)
  1127.             ObtainSemaphore(sem);
  1128.         else if (!AttemptSemaphore(sem)) {
  1129.             Signal(snooptask, missed_curdir_sig);
  1130.             return(CallCurrentDir(newlock));
  1131.         }
  1132.         sendmsg(MSG_CURDIR, newlock, 0);
  1133.         ReleaseSemaphore(sem);
  1134.     }
  1135.     return(CallCurrentDir(newlock));
  1136. }
  1137.  
  1138. /*
  1139.  *        NewDeleteFile()
  1140.  *        ---------------
  1141.  *        Replacement for DeleteFile()
  1142.  */
  1143. __asm LONG NewDeleteFile(reg_d1 char *filename)
  1144. {
  1145.     LONG success;
  1146.     geta4();
  1147.     if (snoopactive && dodelete && FindTask(0) != snooptask) {
  1148.         if (sleepwait)
  1149.             ObtainSemaphore(sem);
  1150.         else if (!AttemptSemaphore(sem)) {
  1151.             Signal(snooptask, missed_delete_sig);
  1152.             return(CallDeleteFile(filename));
  1153.         }
  1154.         sendmsg(MSG_DELETE, 0, filename);
  1155.         success = CallDeleteFile(filename);
  1156.         sendmsg(MSG_DELETE_DONE, success, 0);
  1157.         ReleaseSemaphore(sem);
  1158.     } else
  1159.         return(CallDeleteFile(filename));
  1160. }
  1161.